﻿using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;


namespace Spore.Validator
{

    /// <summary>
    /// 窗体验证器,用于对窗体上的所有具有输入焦点的控件进行验证
    /// </summary>
    public class FormValidator
    {
        Dictionary<Control, List<IValidator>> map;
        List<Control> errorControls;
       

        private ErrorProvider provider;

        /// <summary>
        /// 控件验证完成事件
        /// </summary>
        public event System.Action<Control, bool> ValidCompeleted;
       
        /// <summary>
        /// 是否启动验证
        /// </summary>
        private bool mEnableValidate = true;
        /// <summary>
        /// 创建一个窗体验证器
        /// </summary>
        /// <param name="frmToValid">要验证的窗体</param>
        /// <param name="errProvider">验证所用的errProvider</param>
        public FormValidator(ErrorProvider errProvider)
        {
            this.map = new Dictionary<Control, List<IValidator>>();
            provider = errProvider;
            this.errorControls = new List<Control>();
        }

        /// <summary>
        /// 验证窗体的所有控件
        /// </summary>
        public bool ValidAll()
        {
            if (!this.EnableValidate) return true;
            Control[] controls = this.map.Keys.ToArray();
            bool ret = true;
            List<Control> unCompeletedControls = new List<Control>();
            foreach (Control control in controls)
            {
                if (!control.Enabled | !control.Visible | GetReadOnlyValue(control))
                {
                    this.SetErrorInfo(control, string.Empty);
                    continue;
                }
                foreach (var valid in this.map[control])
                {
                    if (!valid.IsValid(control))
                    {
                        ret = false;
                        this.SetErrorInfo(control, valid.ErrorMessage);
                        unCompeletedControls.Add(control);
                        this.OnValidCompeleted(control, false);
                        break;
                    }
                    else
                    {
                        this.SetErrorInfo(control, string.Empty);
                        this.OnValidCompeleted(control, true);
                    }
                }
            }
            if (!ret)
            {
                unCompeletedControls.RemoveAll((v) => { return v.Parent == null; });
                if (unCompeletedControls.Count != 0)
                {
                    Control control = this.GetFocusControl(unCompeletedControls);
                    if (control != null)
                    {
                        control.Focus();
                        control.Select();
                    }
                }
            }
            return ret;
        }

     
        /// <summary>
        /// 得到需要获得焦点的控件，先按Y坐标排序，再按X坐标排序。
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        private Control GetFocusControl(IEnumerable<Control> source)
        {
                IEnumerable<Control> temps = source.OrderBy<Control, int>((control) =>
                 {
                         Point point = control.Parent.PointToScreen(control.Location);
                         return point.Y;
                 });
                Control c = temps.ElementAt(0);
                Point p = c.Parent.PointToScreen(c.Location);
                temps = temps.Where((param) => {
                        Point pt = param.Parent.PointToScreen(param.Location);
                        return pt.Y == p.Y;
                });
                temps = temps.OrderBy<Control, int>((param) => 
                    {
                            return param.Parent.PointToScreen(param.Location).X;
                    });
                return temps.ElementAt(0);
            
        }

        /// <summary>
        /// 强制验证所有，内部自动打开EnableValidate属性,验证完毕后还原该属性值。
        /// </summary>
        /// <returns>是否验证通过</returns>
        public bool ValidAllForce()
        {
            bool temp = this.EnableValidate;
            this.EnableValidate = true;
            bool ret = this.ValidAll();
            this.EnableValidate = temp;
            return ret;
        }  
        private static bool GetReadOnlyValue(Control control)
        {
            bool readOnly = false;
            System.Type type = control.GetType();
            System.Reflection.PropertyInfo pInfo = type.GetProperty("ReadOnly", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.DeclaredOnly);
            if (pInfo != null)
            {
                readOnly = (bool)pInfo.GetGetMethod().Invoke(control, null);
            }
            else
            {
                 System.Reflection.PropertyInfo[] infos= type.GetProperties();
                 foreach (var p in infos)
                 {
                     if (p.Name == "Properties")
                     {
                         object obj = p.GetGetMethod().Invoke(control, null);
                         pInfo = obj.GetType().GetProperty("ReadOnly", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public);
                         if (pInfo != null)
                         {
                             readOnly = (bool)pInfo.GetGetMethod().Invoke(obj, null);
                             break;
                         }
                     }
                 }
            }
            return readOnly;
        }
        /// <summary>
        /// 如果为true,表示验证启动;如果为false,表示验证没有启动
        /// </summary>
        public bool EnableValidate
        {
            get
            {
                return mEnableValidate;
            }
            set
            {
                if (value == mEnableValidate)
                    return;
                else
                {
                    mEnableValidate = value;
                    foreach (Control control in this.map.Keys)
                    {
                        control.CausesValidation = mEnableValidate;
                    }
                }
            }
        }
        /// <summary>
        /// 为窗体的所有需要验证的控件设置验证规则
        /// </summary>
        /// <param name="controlToValidate">要验证的控件</param>
        /// <param name="rules">验证规则</param>
        public void SetControlValitors(Control controlToValidate, params IValidator[] validators)
        {
            if (!map.ContainsKey(controlToValidate))
            {
                List<IValidator> temp = new List<IValidator>();
                temp.AddRange(validators);
                map.Add(controlToValidate, temp);
                controlToValidate.Validating += new CancelEventHandler(controlToValidate_Validating);
            }
            else
            {
                foreach (var v in validators)
                {
                    System.Type type = v.GetType();
                    var exist =map[controlToValidate].Find((param) => { return type == param.GetType(); });
                    if ( exist==null)
                    {
                        map[controlToValidate].Add(v);
                    }
                    else
                    {
                        map[controlToValidate].Remove(exist);
                        map[controlToValidate].Add(v);
                    }
                }
            }

        }

        void controlToValidate_Validating(object sender, CancelEventArgs e)
        {
            if (!this.EnableValidate) return;
            Control controlToValidate = sender as Control;
            controlToValidate.CausesValidation = true;
            if (!controlToValidate.Enabled | !controlToValidate.Visible | GetReadOnlyValue(controlToValidate))
            {
                this.SetErrorInfo(controlToValidate, string.Empty);
                return;
            }
            foreach (var v in map[controlToValidate])
            {
                if (!v.IsValid(controlToValidate))
                {
                    e.Cancel = false;
                    this.SetErrorInfo(controlToValidate, v.ErrorMessage);
                    this.OnValidCompeleted(controlToValidate, false);
                    return;
                }
                else
                {
                    this.SetErrorInfo(controlToValidate, string.Empty);
                    this.OnValidCompeleted(controlToValidate, true);
                }
            }
        }

        protected virtual void OnValidCompeleted(Control control, bool validResult)
        {
            if (this.ValidCompeleted != null)
                this.ValidCompeleted(control, validResult);
        }

        /// <summary>
        /// 移除控件验证器
        /// </summary>
        /// <param name="controlToValidate"></param>
        /// <param name="validators"></param>
        public void RemoveValitors(Control controlToValidate, params IValidator[] validators)
        {
            if (!map.ContainsKey(controlToValidate))
            {
                return;
            }
            else
            {
                foreach (var v in validators)
                {
                    map[controlToValidate].Remove(v);
                }
            }
        }

        /// <summary>
        /// 移除该控件上的所有验证器
        /// </summary>
        /// <param name="controlToValidate"></param>
        public void RemoveValitors(Control controlToValidate)
        {
            this.map.Remove(controlToValidate);
            controlToValidate.Validating -= new CancelEventHandler(controlToValidate_Validating);
        }

        /// <summary>
        /// 该方法主要解决当需要的验证控件获得焦点且未通过验证时，在取消提交，或清空窗体信息时，焦点不能转移的问题。
        /// 设置不触发验证的控件，比如，取消按钮，清空按钮等等
        /// </summary>
        /// <param name="controls"></param>
        public void SetCancelValidControl(params Control[] controls)
        {
            foreach (var v in controls)
            {
                v.MouseEnter += (arg1, arg2) => { this.EnableValidate = false; };
                v.Leave += (arg1, arg2) => { this.EnableValidate = true; };
            }
        }

        private void SetErrorInfo(Control control,string errorInfo)
        {
            if (!string.IsNullOrEmpty(errorInfo))
            {
                if (!this.errorControls.Contains(control))
                {
                    this.errorControls.Add(control);
                }
                this.provider.SetIconAlignment(control, ErrorIconAlignment.MiddleRight);
                this.provider.SetError(control, errorInfo);
            }
            else
            {
                this.provider.SetError(control, errorInfo);
                if (errorControls.Contains(control))
                {
                    this.errorControls.Remove(control);
                }
            }
        }

       

    }

}
